import { Node, Prefab, SpriteFrame } from 'cc'
import Singleton from '../Base/Singleton'
import {
  IActorMove,
  IBullet,
  IClientInput,
  InputTypeEnum,
  IRoom,
  IState,
  ITimePast,
  IWeaponShoot,
  toFixed
} from '../Common'
import { ActorManager } from '../Entity/Actor/ActorManager'
import { BulletManager } from '../Entity/Bullet/BulletManager'
import { EventEnum } from '../Enum'
import { JoyStickManager } from '../UI/JoyStickManager'
import { randomBySeed } from '../Utils'
import EventManager from './EventManager'

const ACTOR_SPEED = 100
const BULLET_SPEED = 600
const MAP_WIDTH = 960
const MAP_HEIGHT = 640

const ACTOR_RADIUS = 50
const BULLET_RADIUS = 10
const BULLET_DAMAGE = 5

export default class DataManager extends Singleton {
  static get Instance() {
    return super.GetInstance<DataManager>()
  }

  myPlayerId = 1
  frameId = 1
  roomInfo: IRoom

  stage: Node
  jm: JoyStickManager

  actorMap: Map<number, ActorManager> = new Map()
  prefabMap: Map<string, Prefab> = new Map()
  textureMap: Map<string, SpriteFrame[]> = new Map()
  bulletMap: Map<number, BulletManager> = new Map()

  lastState: IState
  state: IState = {
    actors: [
      // {
      //   id: 1,
      //   hp: 100,
      //   type: EntityTypeEnum.Actor1,
      //   weaponType: EntityTypeEnum.Weapon1,
      //   bulletType: EntityTypeEnum.Bullet2,
      //   position: {
      //     x: -150,
      //     y: -150
      //   },
      //   direction: {
      //     x: 1,
      //     y: 0
      //   }
      // },
      // {
      //   id: 2,
      //   hp: 100,
      //   type: EntityTypeEnum.Actor1,
      //   weaponType: EntityTypeEnum.Weapon1,
      //   bulletType: EntityTypeEnum.Bullet2,
      //   position: {
      //     x: 150,
      //     y: 150
      //   },
      //   direction: {
      //     x: -1,
      //     y: 0
      //   }
      // }
    ],
    bullets: [],
    nextBulletId: 1,
    seed: 1
  }

  applyInput(input: IClientInput) {
    switch (input.type) {
      case InputTypeEnum.ActorMove:
        this.handleActorMove(input as IActorMove)
        break
      case InputTypeEnum.WeaponShoot:
        this.handleWeaponShoot(input as IWeaponShoot)
        break
      case InputTypeEnum.TimePast:
        this.handleTimePast(input as ITimePast)
        break
    }
  }

  handleActorMove(input: IActorMove) {
    const {
      id,
      direction: { x, y },
      dt
    } = input

    const actor = this.state.actors.find(a => a.id === id)
    if (actor) {
      actor.direction.x = x
      actor.direction.y = y

      actor.position.x += toFixed(x * dt * ACTOR_SPEED)
      actor.position.y += toFixed(y * dt * ACTOR_SPEED)
    }
  }

  handleWeaponShoot(input: IWeaponShoot) {
    const { owner, position, direction } = input
    const bullet: IBullet = {
      id: this.state.nextBulletId++,
      owner,
      position,
      direction,
      type: this.actorMap.get(owner).bulletType
    }

    EventManager.Instance.emit(EventEnum.BulletBorn, owner)
    this.state.bullets.push(bullet)
  }

  handleTimePast(input: ITimePast) {
    const { dt } = input
    const { bullets, actors } = this.state

    // 子弹碰撞
    for (let i = bullets.length - 1; i >= 0; i--) {
      const bullet = bullets[i]
      // 检测子弹是否击中actor
      for (let j = actors.length - 1; j >= 0; j--) {
        const actor = actors[j]
        if (actor.id === bullet.owner) continue
        // 计算子弹和actor的距离
        const distance = Math.sqrt(
          Math.pow(actor.position.x - bullet.position.x, 2) + Math.pow(actor.position.y - bullet.position.y, 2)
        )
        // 如果子弹和actor的距离小于它们的半径之和,则认为子弹击中actor
        if (distance < ACTOR_RADIUS + BULLET_RADIUS) {
          const random = randomBySeed(this.state.seed)
          // 帧同步，使用随机种子来实现是否暴击
          this.state.seed = random
          const damage = random / 233280 >= 0.5 ? BULLET_DAMAGE * 2 : BULLET_DAMAGE
          actor.hp -= damage
          EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
            x: toFixed((actor.position.x + bullet.position.x) / 2),
            y: toFixed((actor.position.y + bullet.position.y) / 2)
          })
          bullets.splice(i, 1)
          break
        }
      }
      // 子弹超出地图范围,删除
      if (Math.abs(bullet.position.x) > MAP_WIDTH / 2 || Math.abs(bullet.position.y) > MAP_HEIGHT / 2) {
        EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, { x: bullet.position.x, y: bullet.position.y })
        bullets.splice(i, 1)
        break
      }
    }

    // 子弹移动
    for (const bullet of bullets) {
      bullet.position.x += toFixed(bullet.direction.x * dt * BULLET_SPEED)
      bullet.position.y += toFixed(bullet.direction.y * dt * BULLET_SPEED)
    }
  }
}
